#!/usr/bin/env python
# -*- encoding: utf-8 -*-

'''
@File    :   bayes.py  
@Version : 1.0  
@Author :   iherr
@Desciption : None
'''

import numpy as np

def loadDataSet():
    '''
    创建数据集和分类
    :return: 数据集和分类
    '''
    postingList = [['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],
                   ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
                   ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
                   ['stop', 'posting', 'stupid', 'worthless', 'garbage'],
                   ['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
                   ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']] #切分的词条
    classVec = [0, 1, 0, 1, 0, 1]  # 1是侮辱类, 0是非侮辱类
    return postingList, classVec


def createVocabList(dataSet):
    '''
    将数据集里的词条整理成词汇表
    :param dataSet:数据集
    :return:不重复的词汇表
    '''
    vocabSet = set([])  # 创建空的set集合
    for document in dataSet:
        vocabSet = vocabSet | set(document)  # 并集
    return list(vocabSet)


def setOfWords2Vec(vocabList, inputSet):
    '''
    根据词汇表，将切分的词条向量化（每个元素为0或1）
    :param vocabList:词汇表
    :param inputSet:切分的词条
    :return:词条向量
    '''
    returnVec = [0] * len(vocabList)
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] = 1
        else:
            print("the word: %s is not in my Vocabulary!" % word)
    return returnVec


def trainNB0(trainMatrix, trainCategory):
    '''
    朴素贝叶斯分类器训练函数
    :param trainMatrix: 词条向量化构成的矩阵
    :param trainCategory: 类别标签
    :return: p0Vect-非侮辱类条件概率数组，p1Vect-侮辱类条件概率数组，pAbusive-文档属于侮辱类的概率
    '''
    numTrainDocs = len(trainMatrix) # 训练的数据集数量
    numWords = len(trainMatrix[0])  #每个数据集的词条数
    pAbusive = sum(trainCategory) / float(numTrainDocs) #文档属于侮辱类的概率
    p0Num = np.ones(numWords); #词条初始化，为防止0乘以任何数为0，因此初始化为0
    p1Num = np.ones(numWords)  #同上
    p0Denom = 2.0;  # 本来是0开始计数，怕出现分母为0，设置为2。
    p1Denom = 2.0
    for i in range(numTrainDocs):
        if trainCategory[i] == 1: #统计属于侮辱类的条件概率所需的数据，即P(w0|1),P(w1|1),P(w2|1)...
            p1Num += trainMatrix[i]
            p1Denom += sum(trainMatrix[i])
        else:                     #统计属于非侮辱类的条件概率所需的数据，即P(w0|0),P(w1|0),P(w2|0)...
            p0Num += trainMatrix[i]
            p0Denom += sum(trainMatrix[i])
    p1Vect = np.log(p1Num / p1Denom)  # 本来不用log，因为计算概率时有太多很小的数相乘导致下溢出，因此对乘积取自然对数。ln(a*b)=ln(a)+ln(b),f(x)和ln(f(x))两者在某一点的单调性相同。
    p0Vect = np.log(p0Num / p0Denom)
    return p0Vect, p1Vect, pAbusive


def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
    # p1代表的公式如下：P(侮辱 | w0,w1,w2,...) = P(侮辱) × P(w0,w1,w2,... |侮辱) / P(w0,w1,w2,...)
    p1 = sum(vec2Classify * p1Vec) + np.log(pClass1)  # 都是乘积，由于p1Vec已取对数，因此对P(1)也需要同样取对数
    # p0代表的公式如下：P(非侮辱 | w0,w1,w2,...) = P(非侮辱) × P(w0,w1,w2,... |非侮辱) / P(w0,w1,w2,...)
    p0 = sum(vec2Classify * p0Vec) + np.log(1.0 - pClass1)
    if p1 > p0: #只需要比对分子即可，因为分母相同，P1大则归到侮辱类
        return 1
    else:
        return 0
